

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "../mac.h"
#include "aws_iot_client_main.h"

#include "core_mqtt.h"
#include "core_mqtt_state.h"
#include "network_transport.h"
#include "backoff_algorithm.h"

#include "clock.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "aws_iot_Subscribe.h"
#include "aws_iot_Publish.h"
#include "../CRC16.h"
#include "../LCD/Lcd_AWS_Interface.h"

static const char *TAG = "AWS IoT客户端";


/**
 * @brief Packet Identifier generated when Unsubscribe request was sent to the broker;
 * it is used to match received Unsubscribe ACK to the transmitted unsubscribe
 * request.
 */
static uint16_t globalUnsubscribePacketIdentifier = 0U;
/**
 * @brief Array to keep subscription topics.
 * Used to re-subscribe to topics that failed initial subscription attempts.
 */
static MQTTSubscribeInfo_t pGlobalSubscriptionList[1];

static uint16_t globalSubscribePacketIdentifier = 0U;

/**
 * @brief Status of latest Subscribe ACK;
 * it is updated every time the callback function processes a Subscribe ACK
 * and accounts for subscription to a single topic.
 */
MQTTSubAckStatus_t globalSubAckStatus = MQTTSubAckFailure;



char* sub = "command/";
#define sub_Length 8
#define subscribeName_Length (sub_Length + MAC_Length)
char subscribeName[subscribeName_Length];


void SubscribeName(void)
{
    int i = 0;
    for (i = 0; i < sub_Length; i++) subscribeName[i] = sub[i];
    for (; i < subscribeName_Length; i++) subscribeName[i] = WIFI_MAC[i - sub_Length]; 
}

static uint32_t generateRandomNumber()
{
    return (rand());
}

void updateSubAckStatus(MQTTPacketInfo_t *pPacketInfo)
{
    uint8_t *pPayload = NULL;
    size_t pSize = 0;

    MQTTStatus_t mqttStatus = MQTT_GetSubAckStatusCodes(pPacketInfo, &pPayload, &pSize);

    /* MQTT_GetSubAckStatusCodes always returns success if called with packet info
     * from the event callback and non-NULL parameters. */
    assert(mqttStatus == MQTTSuccess);

    /* Suppress unused variable warning when asserts are disabled in build. */
    (void)mqttStatus;

    /* Demo only subscribes to one topic, so only one status code is returned. */
    globalSubAckStatus = pPayload[0];
}

static int handleResubscribe(MQTTContext_t *pMqttContext)
{
    int returnStatus = EXIT_SUCCESS;
    MQTTStatus_t mqttStatus = MQTTSuccess;
    BackoffAlgorithmStatus_t backoffAlgStatus = BackoffAlgorithmSuccess;
    BackoffAlgorithmContext_t retryParams;
    uint16_t nextRetryBackOff = 0U;

    assert(pMqttContext != NULL);

    /* Initialize retry attempts and interval. */
    BackoffAlgorithm_InitializeParams(&retryParams,
                                      500,
                                      5000,
                                      5);

    do
    {
        /* Send SUBSCRIBE packet.
         * Note: reusing the value specified in globalSubscribePacketIdentifier is acceptable here
         * because this function is entered only after the receipt of a SUBACK, at which point
         * its associated packet id is free to use. */
        mqttStatus = MQTT_Subscribe(pMqttContext,
                                    pGlobalSubscriptionList,
                                    sizeof(pGlobalSubscriptionList) / sizeof(MQTTSubscribeInfo_t),
                                    globalSubscribePacketIdentifier);

        if (mqttStatus != MQTTSuccess)
        {
            ESP_LOGE(TAG,"Failed to send SUBSCRIBE packet to broker with error = %s.", MQTT_Status_strerror(mqttStatus));
            returnStatus = EXIT_FAILURE;
            break;
        }

        ESP_LOGE(TAG,"发送 订阅主题\n");

        /* Process incoming packet. */
        mqttStatus = MQTT_ProcessLoop(pMqttContext, 100);

        if (mqttStatus != MQTTSuccess)
        {
            ESP_LOGE(TAG,"MQTT_ProcessLoop returned with status = %s.", MQTT_Status_strerror(mqttStatus));
            returnStatus = EXIT_FAILURE;
            break;
        }

        /* Check if recent subscription request has been rejected. globalSubAckStatus is updated
         * in eventCallback to reflect the status of the SUBACK sent by the broker. It represents
         * either the QoS level granted by the server upon subscription, or acknowledgement of
         * server rejection of the subscription request. */
        if (globalSubAckStatus == MQTTSubAckFailure)
        {
            /* Generate a random number and get back-off value (in milliseconds) for the next re-subscribe attempt. */
            backoffAlgStatus = BackoffAlgorithm_GetNextBackoff(&retryParams, generateRandomNumber(), &nextRetryBackOff);

            if (backoffAlgStatus == BackoffAlgorithmRetriesExhausted)
            {
                ESP_LOGE(TAG,"Subscription to topic failed, all attempts exhausted.");
                returnStatus = EXIT_FAILURE;
            }
            else if (backoffAlgStatus == BackoffAlgorithmSuccess)
            {
                LogWarn(("Server rejected subscription request. Retrying "
                         "connection after %hu ms backoff.",
                         (unsigned short)nextRetryBackOff));
                Clock_SleepMs(nextRetryBackOff);
            }
        }
    } while ((globalSubAckStatus == MQTTSubAckFailure) && (backoffAlgStatus == BackoffAlgorithmSuccess));

    return returnStatus;
}

static int unsubscribeFromTopic(MQTTContext_t *pMqttContext)
{
    int returnStatus = EXIT_SUCCESS;
    MQTTStatus_t mqttStatus;

    assert(pMqttContext != NULL);

    /* Start with everything at 0. */
    (void)memset((void *)pGlobalSubscriptionList, 0x00, sizeof(pGlobalSubscriptionList));

    /* This example subscribes to and unsubscribes from only one topic
     * and uses QOS1. */
    pGlobalSubscriptionList[0].qos = MQTTQoS1;
    pGlobalSubscriptionList[0].pTopicFilter = subscribeName;
    pGlobalSubscriptionList[0].topicFilterLength = subscribeName_Length;

    /* Generate packet identifier for the UNSUBSCRIBE packet. */
    globalUnsubscribePacketIdentifier = MQTT_GetPacketId(pMqttContext);

    /* Send UNSUBSCRIBE packet. */
    mqttStatus = MQTT_Unsubscribe(pMqttContext,
                                  pGlobalSubscriptionList,
                                  sizeof(pGlobalSubscriptionList) / sizeof(MQTTSubscribeInfo_t),
                                  globalUnsubscribePacketIdentifier);

    if (mqttStatus != MQTTSuccess)
    {
        ESP_LOGE(TAG,"Failed to send UNSUBSCRIBE packet to broker with error = %s.", MQTT_Status_strerror(mqttStatus));
        returnStatus = EXIT_FAILURE;
    }
    else
    {
        ESP_LOGE(TAG,"取消发送 已订阅主题");
    }

    return returnStatus;
}


void handleIncomingPublish(MQTTPublishInfo_t *pPublishInfo, uint16_t packetIdentifier)
{
    assert(pPublishInfo != NULL);
    ESP_LOGE(TAG,"传入 QOS : %d.", pPublishInfo->qos);
    if ((pPublishInfo->topicNameLength == subscribeName_Length) && (0 == strncmp(subscribeName, pPublishInfo->pTopicName, pPublishInfo->topicNameLength)))
    {
        ESP_LOGE(TAG,"传入的订阅主题名称: %.*s \n"
                 "传入的订阅主题包ID %u \n"
                 "传入的订阅主题包消息内容 : %.*s \n\n",
                 pPublishInfo->topicNameLength,
                 pPublishInfo->pTopicName,
                 packetIdentifier,
                 (int)pPublishInfo->payloadLength,
                 (const char *)pPublishInfo->pPayload);

        char* token = strtok((char *)pPublishInfo->pPayload, ",");
        if((token != NULL) && (strcmp(token,"$") == 0))
        {
            token = strtok(NULL, ",");
            ESP_LOGE(TAG,"接收的指令 ID: %s", token);
            if(token != NULL) MqttSubscribe_Data.MqttSubscribe_Msg.MqttSubscribe_MsgID = atoi(token);
            token = strtok(NULL, ",");
            ESP_LOGE(TAG,"接收的指令 Param: %s", token);
            if(token != NULL) MqttSubscribe_Data.MqttSubscribe_Msg.result = atoi(token);
            
            Subscribe_CmdHandle(MqttSubscribe_Data.MqttSubscribe_Msg);
            MqttSubscribe_Reset();
        }
        else
        {
            MqttSubscribe_Reset();
            ESP_LOGE(TAG,"指令异常: %s",token);
        }
    }
    else
    {
        ESP_LOGE(TAG,"传入主题名称: %.*s 与订阅的主题不匹配", pPublishInfo->topicNameLength, pPublishInfo->pTopicName);
        MqttSubscribe_Reset();
    }
}

void SubscribeLoop(MQTTContext_t *pMqttContext)
{
    int returnStatus = EXIT_SUCCESS;
    MQTTStatus_t mqttStatus = MQTTSuccess;
    uint8_t publishIndex = 5;

    SubscribeName();
    memset(pGlobalSubscriptionList, 0x00, sizeof(pGlobalSubscriptionList));
    //此示例仅订阅一个主题并使用 QOS1
    pGlobalSubscriptionList[0].qos = MQTTQoS1;
    pGlobalSubscriptionList[0].pTopicFilter = subscribeName;
    pGlobalSubscriptionList[0].topicFilterLength = subscribeName_Length;
    //为订阅数据包生成数据包标识符。
    globalSubscribePacketIdentifier = MQTT_GetPacketId(pMqttContext);
    mqttStatus = MQTT_Subscribe(pMqttContext,pGlobalSubscriptionList, sizeof(pGlobalSubscriptionList) / sizeof(MQTTSubscribeInfo_t), globalSubscribePacketIdentifier);
    if (mqttStatus != MQTTSuccess)
    {
        ESP_LOGE(TAG,"主题订阅包发送 失败 error = %s.", MQTT_Status_strerror(mqttStatus));
        returnStatus = EXIT_FAILURE;
    }
    if (globalSubAckStatus == MQTTSubAckFailure)
    {
        //如果服务器拒绝了订阅请求，请尝试重新订阅主题。根据 retryUtils 中实现的指数退避重试策略进行尝试。
        ESP_LOGE(TAG,"服务器拒绝了初始订阅请求. 尝试重新订阅主题");
        returnStatus = handleResubscribe(pMqttContext);
    }
    if(returnStatus == EXIT_SUCCESS)
    {
        ESP_LOGE(TAG,"成功 订阅MQTT主题");
        OTA_Trigger();
UI_System.ICO_WIFI = UI_ON;
        PublishLoop(pMqttContext); //死循环, 直到 发布异常退出
UI_System.ICO_WIFI = UI_OFF;
    }
}
